ISRT • AST 232 • Reference

R Programming Reference for AST 232

A take home reference for the R you will need across this course. You already have a dedicated R programming course, so nothing here is taught in class. Flip to the relevant section whenever you get stuck during a lab or exam.

Instructor: K.M. Tanvir • Institute of Statistical Research and Training (ISRT), University of Dhaka

Table of Contents

§ 1R and RStudio Setup

Install R from cran.r-project.org (the actual language) and RStudio Desktop from posit.co (a friendly editor around it). Open RStudio; the console at the bottom is where you type commands.

Working directory

R has a "current folder" it reads from and writes to. Check and change it with:

getwd()                              # where am I?
setwd("/Users/you/Desktop/ast232")   # go there (macOS / Linux)
setwd("C:/Users/you/Desktop/ast232")  # go there (Windows)
Best practice. Create one folder per lab or per exam, save your .R or .Rmd file there, then use Session → Set Working Directory → To Source File Location. All your paths become simple: read.csv("data.csv") just works.

§ 2Basic Syntax and Assignment

SymbolMeaningExample
#Comment, everything after # is ignored# this is a note
<-Assignment (preferred in R)x <- 5
=Also assignment, plus used for function argumentsmean(x, na.rm = TRUE)
+ - * /Arithmetic3 + 4 * 2
^Exponent2 ^ 10 gives 1024
%%Modulo (remainder)17 %% 5 gives 2
%/%Integer division17 %/% 5 gives 3
x <- 5                # store 5 in x
y <- 3                # store 3 in y
x + y              # prints 8 to the console
z <- x + y          # store 8 in z, print nothing
print(z)           # prints 8 explicitly

§ 3Data Types

TypeExampleCheck with
numeric (double)3.14, 0.005is.numeric(x)
integer5L, 100Lis.integer(x)
character"male", "V1"is.character(x)
logicalTRUE, FALSEis.logical(x)
factorfactor(c("A","B","A"))is.factor(x)
missingNA, NA_real_, NA_integer_is.na(x)
nullNULLis.null(x)

Factors are important in ANOVA

ANOVA treats a factor differently from a plain number. Always convert treatment / block / group columns to factors before aov():

treat <- c("V1", "V2", "V3", "V1", "V2", "V3")
treat <- factor(treat)
levels(treat)   # "V1" "V2" "V3"
NA and comparisons. Anything compared with NA returns NA, not TRUE or FALSE. Use is.na(x) to test for missing values, not x == NA.

§ 4Vectors

The vector is the fundamental R object. Even a single number is a vector of length 1.

Creating vectors

x <- c(1, 2, 3, 4, 5)              # combine
y <- 1:10                          # sequence 1 to 10
z <- seq(0, 1, by = 0.1)             # 0.0, 0.1, ..., 1.0
w <- seq(0, 100, length.out = 11)   # 0, 10, 20, ..., 100
r <- rep("A", times = 5)             # "A" "A" "A" "A" "A"
g <- rep(c("V1", "V2", "V3"), each = 4)   # V1 V1 V1 V1 V2 V2 ... V3 V3
h <- rep(1:4, times = 3)          # 1 2 3 4 1 2 3 4 1 2 3 4

Vector arithmetic is element wise

a <- c(1, 2, 3)
b <- c(10, 20, 30)
a + b       # 11 22 33
a * 2       # 2  4  6   (scalar broadcasts)
sqrt(a)     # 1.000 1.414 1.732

Handy summary functions

length(x)    # number of elements
sum(x)       # total
mean(x)      # mean
min(x); max(x)
range(x)     # c(min, max)
sort(x)      # sorted vector
rev(x)       # reverse
cumsum(x)    # cumulative sums, used in Life Table Tx
cumprod(x)   # cumulative products, used in Life Table lx

§ 5Data Frames

A data frame is a rectangular table where each column can be a different type. It is the R equivalent of a spreadsheet.

# Build one from vectors
age    <- c(18, 22, 30, 45)
gender <- c("F", "M", "F", "M")
height <- c(160, 175, 162, 180)

df <- data.frame(age, gender = factor(gender), height)
df
#   age gender height
# 1  18      F    160
# 2  22      M    175
# 3  30      F    162
# 4  45      M    180

Exploring a new data frame

head(df, n = 6)    # first 6 rows
tail(df, n = 6)    # last 6 rows
str(df)            # structure: types and first values
summary(df)        # quick summary per column
names(df)          # column names
nrow(df); ncol(df)
dim(df)            # c(nrow, ncol)

Adding a computed column

df$bmi <- 70 / (df$height / 100)^2    # assume weight 70 kg

§ 6Subsetting: [ ], [[ ]], $

Vector subsetting

x <- c(10, 20, 30, 40, 50)

x[3]              # 30 (third element)
x[2:4]            # 20 30 40
x[c(1, 3, 5)]      # 10 30 50 (integer index)
x[-1]             # drop the first element
x[x > 25]          # 30 40 50 (logical index)

Data frame subsetting

df[1, ]              # first row, all columns
df[, 2]              # second column, all rows (returns vector)
df[, "gender"]       # same, by name
df$gender           # same, dollar shortcut

df[df$age > 25, ]     # all rows where age > 25
df[df$gender == "F", c("age", "height")]   # female rows, two columns

[ ] vs [[ ]] in a nutshell

§ 7Reading and Writing Data

CSV files

# Read
dat <- read.csv("my_data.csv")
dat <- read.csv("my_data.csv", header = TRUE,
                stringsAsFactors = FALSE)

# Write
write.csv(dat, "output.csv", row.names = FALSE)

Excel files

install.packages("readxl")           # once
library(readxl)

dat <- read_excel("my_data.xlsx")
dat <- read_excel("my_data.xlsx", sheet = "Sheet2")

R's native format (.RData / .rds)

# .RData: save multiple objects together
save(dat, model, file = "analysis.RData")
load("analysis.RData")    # restores dat, model into workspace

# .rds: one object at a time, cleaner
saveRDS(model, "model.rds")
model <- readRDS("model.rds")

§ 8Control Flow: if, for, while

if / else

x <- 10
if (x > 5) {
  print("big")
} else if (x == 5) {
  print("exactly five")
} else {
  print("small")
}

Vectorised alternative: ifelse()

x <- c(2, 7, 4, 9)
ifelse(x > 5, "big", "small")
# "small" "big"   "small" "big"

for loop

for (i in 1:5) {
  print(i^2)
}

# Fill a vector inside a loop (pre allocate)
result <- numeric(10)
for (i in 1:10) {
  result[i] <- i^2
}

while loop

i <- 1
while (i <= 5) {
  print(i)
  i <- i + 1
}
Prefer vectorised code over loops. R is designed for vectors. Writing x * 2 is faster and clearer than looping over each element and multiplying. Reach for a loop only when you cannot express the task vectorised.

§ 9Writing Your Own Functions

The syntax is function_name <- function(arg1, arg2, ...) { ... body ... }. The last expression evaluated is the return value (you can also use return()).

# A tiny function
square <- function(x) {
  x ^ 2
}
square(7)   # 49

# Default arguments
mean_or_median <- function(x, use_median = FALSE) {
  if (use_median) median(x) else mean(x)
}

# A function that returns multiple values via a named list
summary_stats <- function(x) {
  list(
    n       = length(x),
    mean    = mean(x),
    sd      = sd(x),
    range   = range(x)
  )
}
summary_stats(c(1, 4, 5, 6, 8))
You wrote functions like these on the labs. build_life_table() in Module 4 and mortality_report() in Module 3 are both custom functions that bundle a workflow into one reusable name.

§ 10The Apply Family

These functions apply another function across the elements of a vector, list, or margin of a matrix / data frame. They replace many for loops.

FunctionApplies toReturnsExample
apply(mat, MARGIN, FUN)Matrix or data frameVector or matrixapply(m, 1, sum) row sums
sapply(x, FUN)Vector or listSimplified vector / matrixsapply(1:5, function(i) i^2)
lapply(x, FUN)Vector or listList (always)lapply(mylist, mean)
tapply(x, group, FUN)Vector split by groupNamed vector / arraytapply(yield, treat, mean)
mapply(FUN, x, y, ...)Multiple vectors in parallelVectormapply(sum, 1:3, 4:6)
tapply is the workhorse of Block 2. Any time you want a group mean, group sum, or group count, tapply(y, group, FUN) is the natural R idiom. It appears in every CRD, RCBD, and LSD analysis.

§ 11Common Statistical Functions

FunctionMeaning
mean(x)Arithmetic mean
median(x)Median
var(x)Sample variance (divides by n − 1)
sd(x)Sample standard deviation
quantile(x, probs)Quantiles at the given probabilities
IQR(x)Interquartile range Q3 − Q1
cor(x, y)Pearson correlation
cov(x, y)Covariance
summary(x)Five number summary plus mean
table(x)Frequency table
prop.table(table(x))Proportions instead of counts
na.rm = TRUEArgument to skip NA values, e.g. mean(x, na.rm = TRUE)

§ 12Distribution Functions

Every standard distribution comes with four functions, prefixed d (density), p (cumulative), q (quantile / inverse cumulative), and r (random draws).

Distributiond / p / q / rWhat you use it for
Normal(μ, σ)dnorm, pnorm, qnorm, rnormZ tests, confidence intervals
Student tdt, pt, qt, rtt tests, one at a time CIs
F(df1, df2)df, pf, qf, rfANOVA F tests
Chi squared(df)dchisq, pchisq, qchisq, rchisqGoodness of fit, variance CIs
Binomial(n, p)dbinom, pbinom, qbinom, rbinomYes / no counts
Poisson(λ)dpois, ppois, qpois, rpoisEvent counts (birth or death counts in demography)

How the four flavours differ

dnorm(1.96)              # density (height of the curve) at x = 1.96
pnorm(1.96)              # P(Z <= 1.96) = 0.975
qnorm(0.975)             # quantile: value with P below it of 0.975 = 1.96
rnorm(5, mean = 0, sd = 1) # 5 random draws
How Block 2 uses this. The critical F value in an ANOVA is qf(0.95, df_treat, df_error). A one at a time CI in Module 5 uses qt(0.975, df_error). Bonferroni CIs replace 0.975 with 1 − α / (2r).

§ 13Formulas and aov()

A formula in R uses ~ to separate the response (left) from the predictors (right).

FormulaMeaningDesign
y ~ treatResponse depends on one factorCRD
y ~ treat + blockResponse depends on treatment and block (main effects)RCBD
y ~ treat + row + columnThree main effectsLatin Square
y ~ treat * blockMain effects plus interaction (equivalent to treat + block + treat:block)Factorial (not covered)
y ~ .Response against every other column in the dataRegression shortcut
model <- aov(yield ~ treat + block, data = df)
summary(model)               # ANOVA table with F and p
residuals(model)             # residuals for diagnostics
fitted(model)                # fitted values
coef(model)                  # coefficients (contrast form)
TukeyHSD(model, "treat")     # pairwise comparison, family wise controlled

§ 14Plotting Basics

Base R plotting functions used in this course

FunctionWhat it drawsWhere it appears
plot(x, y)Scatter or line plotASFR and ASDR curves, ex curve
boxplot(y ~ x)Boxplot by groupCRD, RCBD visualisation
hist(x)HistogramResidual distribution check
barplot(x)Bar chart, accepts negativesPopulation pyramid
qqnorm(x); qqline(x)Normal QQ plotNormality check in ANOVA
interaction.plot()Mean of y by two factorsRCBD dye trial exercise
plot(model)Four diagnostic panelsShortcut for CRD / RCBD checks

Common styling arguments

plot(x, y,
     type = "o",           # "p" points, "l" lines, "b" both, "o" over-plotted
     pch  = 19,            # point shape (19 = filled circle)
     col  = "#2563eb",     # colour (name, hex, or rgb)
     lwd  = 2,             # line width
     xlab = "Age group",   # axis labels
     ylab = "Rate",
     main = "My plot",     # title
     xlim = c(0, 50),      # axis range
     ylim = c(0, 100))
points(x, y2, col = "red")     # overlay more points
lines(x, y3, col = "green")   # overlay a line
abline(h = 0, lty = 2)         # horizontal dashed reference line
legend("topright",
       legend = c("A", "B"),
       col    = c("red", "green"),
       lty    = 1, lwd = 2)

Multiple plots in one window

par(mfrow = c(1, 2))   # 1 row, 2 columns
qqnorm(res); qqline(res)
plot(fit, res); abline(h = 0, lty = 2)
par(mfrow = c(1, 1))   # reset back to single plot

§ 15Common Pitfalls

1. Character used where a factor is needed

If treat is a character vector (not a factor), aov() may still work but printing coefficients gets confusing. Convert with factor() before modelling.

2. Missing values silently propagate

mean(x) where x contains a single NA returns NA. Add na.rm = TRUE: mean(x, na.rm = TRUE).

3. Integer overflow

Very large integer arithmetic can overflow. Use doubles: multiply by 1.0 to force conversion.

4. Case sensitive names

Yield is not the same as yield. R errors will complain "object not found". Check your capitalisation.

5. Wrong assignment direction

5 -> x is legal R (right assignment) but almost always a mistake. Stick with <- or =.

6. Forgetting to reset par()

After par(mfrow = c(2, 2)), R stays in 2x2 layout until you set it back to c(1, 1) or restart R.

7. Working directory drift

If read.csv("data.csv") fails with "cannot open connection", your working directory is not what you think. Check with getwd(), fix with setwd() or (better) use RStudio's Session → Set Working Directory → To Source File Location.

8. Package not loaded

Installed once with install.packages("readxl"), but you still need library(readxl) at the top of every script that uses it.

Debugging tip. When an error hits, look at the last function name mentioned. Then check the arguments you passed to it, especially their types. 80% of R errors are type mismatches or misspelled column names.
↑ Back to top